﻿using System;
using System.Collections;
using System.Runtime.Serialization;
using CI.Interfaces;
using CI.Organism;

namespace CI.Organism
{
    /// <summary>
    /// Heart beat offset from the creation of a cell.
    /// Requirement item that has to be met before the Gene action can be activated
    /// </summary>
    [DataContract]
    public class HBOffsetTimeCell : HBOffsetTime
    {
        /// <summary>
        /// Cell realtive to take the time from
        /// </summary>
        [DataMember]
        public ICell Cell { get; set; }

        /// <summary>
        /// Does the requirement type meet the requirements
        /// </summary>
        /// <returns>True if it meets the requirements else false</returns>
        public override bool State()
        {
            // if comparison type Equals
            if (ComparisonType == ComparisonType.Equal)
                return ((Organism.HeartbeatCount == Cell.HBTSAlive + HBOffset) && (Cell.Active));

            // if comparison type greater
            if (ComparisonType == ComparisonType.GreaterThan)
                return ((Organism.HeartbeatCount > Cell.HBTSAlive + HBOffset) && (Cell.Active));

            // if comparison type less
            if (ComparisonType == ComparisonType.LessThan)
                return ((Organism.HeartbeatCount < Cell.HBTSAlive + HBOffset) && (Cell.Active));

            // else didnt make it
            return false;

        }

        /// <summary>
        ///  Heart beat offset from the creation of a cell
        /// </summary>
        /// <param name="organism">Organism</param>
        /// <param name="hbOffset">Heartbeat offset</param>
        /// <param name="cell">Cell relative to</param>
        /// <param name="comparisonType">Comparison Type</param>
        /// <param name="guid">guid</param>
        public HBOffsetTimeCell(IOrganism organism, UInt32 hbOffset, ICell cell, ComparisonType comparisonType, Guid guid)
            : base(organism, hbOffset, comparisonType, guid)
        {
            #region Argument check
            if (organism == null)
                throw new ArgumentException("Organism");
            if (cell == null)
                throw new ArgumentException("Cell");
            #endregion

            // save the cell
            Cell = cell;
        }



        /// <summary>
        /// Generic constructor only to be used by mutation
        /// After constructored it shoule be mutated with the appropiate mutation rate
        /// It sets the HBOffset to 0
        /// It sets the cell to be null
        /// </summary>
        /// <param name="organism">Organism</param>
        /// <param name="comparisonType">Comparison Type</param>
        /// <param name="guid">guid</param>
        /// todo : change this to internal and fix the caller
        public HBOffsetTimeCell(IOrganism organism, ComparisonType comparisonType, Guid guid) : base(organism, comparisonType, guid) { }

        /// <summary>
        /// Take this parent Requirement and create a new requirement to be put in the new child organism
        /// Mutate the current requirement and return a new one, deep class level mutation
        /// </summary>
        /// <param name="mutationRate">mutation rate to be applied</param>
        /// <param name="childOrganism">child Organism it will be going into</param>
        /// <returns>new requirement for child</returns>
        public override IRequirement Mutate(double mutationRate, IOrganism childOrganism)
        {
            #region Argument check
            if (childOrganism == null)
                throw new ArgumentException("Organism");
            #endregion

            // this should only happen if this class was created through mutation and the limited ctor and now needs a cell added
            if (Cell == null)
            {
                Cell = childOrganism.Cells[Mutation.CollectionSelect((ICollection)childOrganism.Cells)];
            }

            // create a new requirement
            var childRequirement = new HBOffsetTimeCell(childOrganism, HBOffset, childOrganism.Cells.Find(Cell.Guid), ComparisonType, Guid);

            // mutate each of the fields           
            // change the HBOffset amount, force if empty
            do
            {
                childRequirement.HBOffset = Mutation.MutateCrawlOrLeap(HBOffset, mutationRate);
                // if this.HBOffset is 0 and mutation rate is 0 this will loop for infinity, so create a random new offset
                if ((childRequirement.HBOffset == 0) && (mutationRate == 0) && (HBOffset == 0))
                {
                    // set it for a large mutation rate
                    childRequirement.HBOffset = Mutation.MutateCrawlOrLeap(HBOffset, 1);
                }
            }
            while (childRequirement.HBOffset == 0);

            // change the comparison type
            if (Mutation.MutateState(mutationRate))
            {
                childRequirement.ComparisonType = ((ComparisonType)(new Random().Next(1, Enum.GetValues(typeof(ComparisonType)).Length)));
            }

            return childRequirement;
        }

        /// <summary>
        /// To String, show compairson type
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return base.ToString() + Environment.NewLine +
                   string.Format("Cell : {0}", Cell.GetType());
        }

        /// <summary>
        /// do a equals comparison of one requirement type to another only comparing the value types, no refference comparisons
        /// </summary>
        /// <param name="requirement">requirement to compare against</param>
        /// <returns>true if equal value types</returns>
        public override bool EqualsValueTypes(IRequirement requirement)
        {
            // compare value
            if (HBOffset != ((HBOffsetTimeCell)requirement).HBOffset)
                return false;
            if (Cell.Guid != ((HBOffsetTimeCell)requirement).Cell.Guid)
                return false;

            return base.EqualsValueTypes(requirement);
        }

    }
}